/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

using System;

using ReferenceTypeMap = argot.ReferenceTypeMap;
using TypeElement = argot.TypeElement;
using TypeException = argot.TypeException;
using TypeInputStream = argot.TypeInputStream;
using TypeOutputStream = argot.TypeOutputStream;
using TypeReader = argot.TypeReader;
using TypeReaderAuto = argot.TypeReaderAuto;
using TypeLibrary = argot.TypeLibrary;
using TypeWriter = argot.TypeWriter;

namespace argot.meta
{
	
	public class MetaMap
	:MetaBase, TypeReader, TypeWriter, MetaExpression, MetaDefinition
	{
		override public System.String TypeName
		{
			get
			{
				return TYPENAME;
			}
			
		}
		public const System.String TYPENAME = "meta.map";
		
		private TypeLibrary _library;
		
		private int _abstractType;
		private int _concreteType;
		
		private MetaAbstract _metaAbstract;
		
		public MetaMap(int abstractType, int concreteType)
		{
			_abstractType = abstractType;
			_concreteType = concreteType;
		}

		/* required for auto constructor */
		public MetaMap(UInt16 abstractType, UInt16 concreteType)
		{
			_abstractType = (int) abstractType;
			_concreteType = (int) concreteType;
		}
		
		public virtual int GetExpressionType(TypeLibrary library)
		{
			return library.GetId(TYPENAME);
		}
		
		
		public virtual void Check(TypeLibrary library)
		{
			_library = library;
			
			if (_library.GetTypeState(_abstractType) == TypeLibrary.TYPE_NOT_DEFINED)
			{
				throw new TypeException("abstract type not valid");
			}
			
			if (_library.GetTypeState(_concreteType) == TypeLibrary.TYPE_NOT_DEFINED)
			{
				throw new TypeException("concrete type not valid");
			}
			
			MetaExpression abstractElement = (MetaExpression) _library.GetStructure(_abstractType);			
			if (!(abstractElement is MetaAbstract))
			{
				throw new TypeException("abstract map to non-abstract type");
			}
			
			_metaAbstract = (MetaAbstract) abstractElement;
		}
		
		public override void Bind(TypeLibrary library, TypeElement definition, System.String typeName, int typeId)
		{
			base.Bind(library, definition, typeName, typeId);
			Check(library);
			_metaAbstract.AddMap(_concreteType, typeId);
		}
		
		public virtual int GetAbstractType()
		{
			return _abstractType;
		}
		
		public virtual int GetConcreteType()
		{
			return _concreteType;
		}
		
		private void SetConcreteType(int i)
		{
			_concreteType = i;
		}
		
		private void  SetAbstractType(int i)
		{
			_abstractType = i;
		}
		

		public virtual System.Object Read(TypeInputStream inStream, TypeElement element)
		{
			// was instanceof MetaDefinition.  maybe wrong.
			if (element is MetaExpression)
			{
				TypeReader reader = new TypeReaderAuto(this.GetType());
				MetaMap map = (MetaMap) reader.Read(inStream, element);
				// before we return this we need to change the
				// values from the mapped to the internal values.
				
				ReferenceTypeMap mapCore = (ReferenceTypeMap) inStream.TypeMap;
				
				if (mapCore.ReferenceMap.IsValid(map.GetAbstractType()))
				{
					map.SetAbstractType(mapCore.ReferenceMap.GetSystemId(map.GetAbstractType()));
				}
				else
				{
					throw new TypeException("TypeReference: invalid id ");
				}
				
				if (mapCore.ReferenceMap.IsValid(map.GetConcreteType()))
				{
					map.SetConcreteType(mapCore.ReferenceMap.GetSystemId(map.GetConcreteType()));
				}
				else
				{
					throw new TypeException("TypeReference: invalid id ");
				}
				return map;
			}
			throw new TypeException("shouldn't get here.");
		}

		public virtual void Write(TypeOutputStream outStream, System.Object o, TypeElement element)
		{
			MetaMap tr = (MetaMap) o;
			ReferenceTypeMap mapCore = (ReferenceTypeMap) outStream.TypeMap;
			int abstractId = mapCore.ReferenceMap.GetId(tr._abstractType);
			outStream.WriteObject("u16", (System.Object) abstractId);
			int concreteId = mapCore.ReferenceMap.GetId(tr._concreteType);
			outStream.WriteObject("u16", (System.Object) concreteId);
		}
		
		public virtual void DoWrite(TypeOutputStream outStream, System.Object o)
		{
			// need to write out the ID here..
			outStream.WriteObject("u16", (System.Object) 1);
			// then write the object.
			outStream.WriteObject(_concreteType, o);
		}
		
		public virtual System.Object DoRead(TypeInputStream inStream)
		{
			// This probably shouldn't happen.
			// Someone reads an abstract type.  Which checks its local
			// list and reads directly.  
			
			//  But just in case. Just read the concrete type.
			return inStream.ReadObject(inStream.TypeMap.GetId(_concreteType));
		}
	}
}